#include "glwidget.h"
#include <QGLWidget>
#include <GL/glut.h>
#include <GL/freeglut_ext.h>
#include <vector>
#include "pipelistener.h"

using namespace std;
//extern DrawData* gp_DrawData;
//extern typedef s_rect;

//extern vector<s_rect> g_shapes;

extern vector<C_CLB_3D> V_CLBs;
extern vector<s_line> V_LINEs;

GLWidget::GLWidget(QWidget *parent) :
    QGLWidget(parent)
{
    rotationX = 0.0;
    rotationY = 0.0;
    rotationZ = 0.0;
    eyeX = -100.0;
    eyeY = -100.0;
    eyeZ = 0.0;
    move = 30;
    connect(&timer,SIGNAL(timeout()),this,SLOT(updateGL()));
    timer.start(16);

    moveX = 0.0;
    moveY = 0.0;
    moveZ = 0.0;
}

void GLWidget::initializeGL()
{
    init_pipe();
    //glutInitWindowSize (500, 500);
    initializeGLFunctions();
    //qglClearColor(Qt::black);
    glClearColor(0.9,0.9,0.9,1);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHT0);

     glEnable(GL_LIGHTING);
     glEnable(GL_COLOR_MATERIAL);
    //glClearColor(0.2,0.2,0.2,1);
    //glOrtho(0, 960,540, 0, 1, -1);
}
void GLWidget::paintGL()
{
    float x0,y0,z0;
    float x1,y1,z1;
    vector<unsigned int> fanout;

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(eyeX,eyeY,eyeZ,eyeX+101,eyeY+101,eyeZ+1,0,0,1);
    
    glRotatef(rotationX, 1.0, 0.0, 0.0);
    glRotatef(rotationY, 0.0, 1.0, 0.0);
    glRotatef(rotationZ, 0.0, 0.0, 1.0);

    glColor3d(0,0,0);
    glLineWidth(1.0);
        glBegin(GL_LINES);
        glVertex2f( 0.0, 0.0);
        glVertex2f( 100.0, 0.0);
        glVertex2f( 0.0, 0.0);
        glVertex2f( 0.0, 100.0);
    glEnd();

    //update_cube_state();
    parse_cmds();

    float r,g,b;

    // draw cube
    for (int i=0;i<V_CLBs.size();i++) {
        V_CLBs[i].get_loc(x0,y0,z0);
        V_CLBs[i].getColor(r,g,b);
        //printf("draw cube (%f,%f,%f)\n",x,y,z);
        drawcube(x0,y0,z0,GL_POLYGON,r,g,b);
        //drawcube(0.0,0.0,0.0,GL_LINE_LOOP);
    }

    // draw edge
    for (int i=0;i<V_CLBs.size();i++) {
        
        V_CLBs[i].get_loc(x0,y0,z0);
        V_CLBs[i].getColor(r,g,b);

        fanout.clear();
        V_CLBs[i].getFanout(fanout);

        for (int j=0;j<fanout.size();j++) {
            V_CLBs[fanout[j]].get_loc(x1,y1,z1);
            // set color to yellow
            drawline(x0,y0,z0,x1,y1,z1,1.0,1.0,0.0,1.0);
        }

    }

    // draw annotation lines
    for (int i=0;i<V_LINEs.size();i++) {
        
        drawline(V_LINEs[i].x0,V_LINEs[i].y0,V_LINEs[i].z0,
            V_LINEs[i].x1,V_LINEs[i].y1,V_LINEs[i].z1,
            V_LINEs[i].r,V_LINEs[i].g,V_LINEs[i].b,1.0);
    }

}

void GLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    //glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    printf("resize\n");
   // gluPerspective(45.0,(float)(w/h),0.01,100.0);

    //glMatrixMode(GL_MODELVIEW);
    //glLoadIdentity();
    gluLookAt(0,100,0,0,-100,0,0,0,1);
    // Calculate aspect ratio
        //qreal aspect = qreal(w) / qreal(h ? h : 1);

        // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
        //const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;

        // Reset projection
        //projection.setToIdentity();

        // Set perspective projection
        //projection.perspective(fov, aspect, zNear, zFar);
}

void GLWidget::keyPressEvent(QKeyEvent *e) {
    
    float scale = 1.0;

    switch(e->key()) {
        case Qt::Key_W:
            moveX = scale;
            moveY = scale;
            break;
        case Qt::Key_A:
            moveX = -1.0*scale;
            moveY = 1.0*scale;
            break;
        case Qt::Key_D:
            moveX = 1.0*scale;
            moveY = -1.0*scale;
            break;
        case Qt::Key_S:
            moveX = -1.0*scale;
            moveY = -1.0*scale;
            break;
        case Qt::Key_O: // Back to original position
            eyeX = -100.0;
            eyeY = -100.0;
            eyeZ = 0.0;
            break;
        case Qt::Key_Space:
            moveZ = scale;
            break;
        case Qt::Key_Control:
            moveZ = -1.0*scale;
            break;
        default:
            break;
    }

    eyeX = eyeX + moveX;
    eyeY = eyeY + moveY;
    eyeZ = eyeZ + moveZ;
    
}

void GLWidget::keyReleaseEvent(QKeyEvent *e) {
    moveX = 0.0;
    moveY = 0.0;
    moveZ = 0.0;
}

void GLWidget::mousePressEvent(QMouseEvent *e) {
    lastPos = e->pos();
}

void GLWidget::mouseMoveEvent(QMouseEvent *e) {

    GLfloat dx = GLfloat(e->x()-lastPos.x())/width();
    GLfloat dy = GLfloat(e->y()-lastPos.y())/height();

    if (e->buttons() & Qt::LeftButton) {
        rotationX += 180*dy;
        rotationY += 180*dx;
        updateGL();
    } else if (e->buttons() & Qt::RightButton) {
        rotationX += 180*dy;
        rotationY += 180*dx;
    }
    lastPos = e->pos();
}

void GLWidget::wheelEvent ( QWheelEvent * event ) {

    int delta = event->delta()/120;
    move = move+delta;
    if (move<1.0)
        move = 1.0;
    //printf("AA:: %f\n",move+delta);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(move,(GLfloat)width()/(GLfloat)height(),0.1,1000.0);

    glMatrixMode(GL_MODELVIEW);

}

void GLWidget::drawcube(double x,double y, double layer,GLenum LN_Mode) {
    drawcube(x,y,layer,LN_Mode,0.8,0.8,0.8);
}

void GLWidget::drawcube(double x,double y, double layer,GLenum LN_Mode,
    float r, float g, float b) {

    glLineWidth(2.0);
    
    glColor3f(r,g,b);
    float thickscale = 0.5;
    // light blue - TOP
    glBegin(LN_Mode);
    glVertex3f(  0.1+x, -0.1+y, 0.1*thickscale+layer );
    glVertex3f(  0.1+x,  0.1+y, 0.1*thickscale+layer );
    glVertex3f( -0.1+x,  0.1+y, 0.1*thickscale+layer );
    glVertex3f( -0.1+x, -0.1+y, 0.1*thickscale+layer );
    glEnd();

    // light blue - BOTTOM
    glBegin(LN_Mode);
    glVertex3f(  0.1+x, -0.1+y, -0.1*thickscale+layer );
    glVertex3f(  0.1+x,  0.1+y, -0.1*thickscale+layer );
    glVertex3f( -0.1+x,  0.1+y, -0.1*thickscale+layer );
    glVertex3f( -0.1+x, -0.1+y, -0.1*thickscale+layer );
    glEnd();

    // Purple side - RIGHT
    glBegin(LN_Mode);
    glVertex3f( 0.1+x, -0.1+y, -0.1*thickscale+layer );
    glVertex3f( 0.1+x,  0.1+y, -0.1*thickscale+layer );
    glVertex3f( 0.1+x,  0.1+y,  0.1*thickscale+layer );
    glVertex3f( 0.1+x, -0.1+y,  0.1*thickscale+layer );
    glEnd();

    // Green side - LEFT
    glBegin(LN_Mode);
    glVertex3f( -0.1+x, -0.1+y,  0.1*thickscale+layer );
    glVertex3f( -0.1+x,  0.1+y,  0.1*thickscale+layer );
    glVertex3f( -0.1+x,  0.1+y, -0.1*thickscale+layer );
    glVertex3f( -0.1+x, -0.1+y, -0.1*thickscale+layer );
    glEnd();

    // Blue side - FRONT
    glBegin(LN_Mode);
    glVertex3f(  0.1+x,  0.1+y,  0.1*thickscale+layer );
    glVertex3f(  0.1+x,  0.1+y, -0.1*thickscale+layer );
    glVertex3f( -0.1+x,  0.1+y, -0.1*thickscale+layer );
    glVertex3f( -0.1+x,  0.1+y,  0.1*thickscale+layer );
    glEnd();

    // Red side - BACK
    glBegin(LN_Mode);
    glVertex3f(  0.1+x, -0.1+y, -0.1*thickscale+layer );
    glVertex3f(  0.1+x, -0.1+y,  0.1*thickscale+layer );
    glVertex3f( -0.1+x, -0.1+y,  0.1*thickscale+layer );
    glVertex3f( -0.1+x, -0.1+y, -0.1*thickscale+layer );
    glEnd();

    glLineWidth(1.0);
    //write_string(x,y,"0");
}

void GLWidget::drawline(float x0,float y0, float z0,
    float x1,float y1, float z1,
    float r, float g, float b,float lineWidth) {

    glColor3d(r,g,b);
    glLineWidth(lineWidth);
        glBegin(GL_LINES);
        glVertex3f( x0,y0,z0);
        glVertex3f( x1,y1,z1);
    glEnd();
}

////////////////////////////////////////////////////
// C_CLB_3D
////////////////////////////////////////////////////
C_CLB_3D::C_CLB_3D(const char* name, int x, int y, int z) {
    _x = (float) x;
    _y = (float) y;
    _z = (float) z;
    _name = string(name);
    
    _r = 0.7;
    _b = 0.7;
    _g = 0.7;
}

C_CLB_3D::C_CLB_3D(const char* name, float x, float y, float z) {
    _x = x;
    _y = y;
    _z = z;
    _name = string(name);

    _r = 0.7;
    _b = 0.7;
    _g = 0.7;
}

void C_CLB_3D::get_loc(float& x,float& y,float& z) {
    x = _x;
    y = _y;
    z = _z;
}

void C_CLB_3D::set_loc(float x,float y,float z) {
    _x = x;
    _y = y;
    _z = z;
}

const char* C_CLB_3D::getName(void) {
    return _name.data();
}

void C_CLB_3D::setColor(float r,float g,float b) {
    _r = r;
    _g = g;
    _b = b;
}

void C_CLB_3D::getColor(float& r,float& g,float& b) {
    r = _r;
    g = _g;
    b = _b;
}

void C_CLB_3D::addFanout(unsigned int idx) {
    _fanout.push_back(idx);    
}

bool C_CLB_3D::getFanout(vector<unsigned int>& cellIdxs) {
    if (_fanout.size() == 0)
        return false;
    else
        cellIdxs = _fanout;
    
    return true;
}

